"""
缓存服务
"""
import json
from datetime import datetime, timedelta
from typing import List, Optional, Dict, Any
from loguru import logger

from app.utils.redis_client import redis_client
from app.schemas.tracking import RealTimeLocationResponse


class CacheService:
    """缓存服务类"""
    
    # 缓存键前缀
    DEVICE_LOCATION_PREFIX = "device_location"
    DEVICE_STATUS_PREFIX = "device_status"
    GEOFENCE_PREFIX = "geofence"
    TRACKING_STATS_PREFIX = "tracking_stats"
    REALTIME_LOCATIONS_KEY = "realtime_locations"
    
    # 缓存过期时间（秒）
    LOCATION_EXPIRE = 300  # 5分钟
    STATUS_EXPIRE = 600    # 10分钟
    STATS_EXPIRE = 1800    # 30分钟
    
    @classmethod
    async def cache_device_location(
        cls, 
        device_id: str, 
        location_data: Dict[str, Any]
    ) -> bool:
        """缓存设备位置"""
        try:
            cache_key = f"{cls.DEVICE_LOCATION_PREFIX}:{device_id}"
            
            # 添加缓存时间戳
            location_data["cached_at"] = datetime.now().isoformat()
            
            result = await redis_client.set(
                cache_key, 
                location_data, 
                expire=cls.LOCATION_EXPIRE
            )
            
            if result:
                logger.debug(f"缓存设备位置: {device_id}")
            
            return result
        except Exception as e:
            logger.error(f"缓存设备位置失败: {e}")
            return False
    
    @classmethod
    async def get_device_location(cls, device_id: str) -> Optional[Dict[str, Any]]:
        """获取设备位置缓存"""
        try:
            cache_key = f"{cls.DEVICE_LOCATION_PREFIX}:{device_id}"
            location_data = await redis_client.get(cache_key)
            
            if location_data:
                logger.debug(f"获取设备位置缓存: {device_id}")
            
            return location_data
        except Exception as e:
            logger.error(f"获取设备位置缓存失败: {e}")
            return None
    
    @classmethod
    async def cache_device_status(
        cls, 
        device_id: str, 
        status_data: Dict[str, Any]
    ) -> bool:
        """缓存设备状态"""
        try:
            cache_key = f"{cls.DEVICE_STATUS_PREFIX}:{device_id}"
            
            # 添加缓存时间戳
            status_data["cached_at"] = datetime.now().isoformat()
            
            result = await redis_client.set(
                cache_key, 
                status_data, 
                expire=cls.STATUS_EXPIRE
            )
            
            if result:
                logger.debug(f"缓存设备状态: {device_id}")
            
            return result
        except Exception as e:
            logger.error(f"缓存设备状态失败: {e}")
            return False
    
    @classmethod
    async def get_device_status(cls, device_id: str) -> Optional[Dict[str, Any]]:
        """获取设备状态缓存"""
        try:
            cache_key = f"{cls.DEVICE_STATUS_PREFIX}:{device_id}"
            status_data = await redis_client.get(cache_key)
            
            if status_data:
                logger.debug(f"获取设备状态缓存: {device_id}")
            
            return status_data
        except Exception as e:
            logger.error(f"获取设备状态缓存失败: {e}")
            return None
    
    @classmethod
    async def cache_realtime_locations(
        cls, 
        locations: List[RealTimeLocationResponse]
    ) -> bool:
        """缓存实时位置列表"""
        try:
            # 转换为字典列表
            locations_data = [location.dict() for location in locations]
            
            result = await redis_client.set(
                cls.REALTIME_LOCATIONS_KEY,
                {
                    "locations": locations_data,
                    "cached_at": datetime.now().isoformat(),
                    "count": len(locations_data)
                },
                expire=cls.LOCATION_EXPIRE
            )
            
            if result:
                logger.debug(f"缓存实时位置列表: {len(locations_data)} 个设备")
            
            return result
        except Exception as e:
            logger.error(f"缓存实时位置列表失败: {e}")
            return False
    
    @classmethod
    async def get_realtime_locations(cls) -> Optional[List[Dict[str, Any]]]:
        """获取实时位置列表缓存"""
        try:
            cached_data = await redis_client.get(cls.REALTIME_LOCATIONS_KEY)
            
            if cached_data and "locations" in cached_data:
                logger.debug(f"获取实时位置列表缓存: {cached_data.get('count', 0)} 个设备")
                return cached_data["locations"]
            
            return None
        except Exception as e:
            logger.error(f"获取实时位置列表缓存失败: {e}")
            return None
    
    @classmethod
    async def cache_tracking_statistics(cls, stats: Dict[str, Any]) -> bool:
        """缓存追踪统计数据"""
        try:
            stats["cached_at"] = datetime.now().isoformat()
            
            result = await redis_client.set(
                cls.TRACKING_STATS_PREFIX,
                stats,
                expire=cls.STATS_EXPIRE
            )
            
            if result:
                logger.debug("缓存追踪统计数据")
            
            return result
        except Exception as e:
            logger.error(f"缓存追踪统计数据失败: {e}")
            return False
    
    @classmethod
    async def get_tracking_statistics(cls) -> Optional[Dict[str, Any]]:
        """获取追踪统计数据缓存"""
        try:
            stats = await redis_client.get(cls.TRACKING_STATS_PREFIX)
            
            if stats:
                logger.debug("获取追踪统计数据缓存")
            
            return stats
        except Exception as e:
            logger.error(f"获取追踪统计数据缓存失败: {e}")
            return None
    
    @classmethod
    async def invalidate_device_cache(cls, device_id: str) -> bool:
        """清除设备相关缓存"""
        try:
            location_key = f"{cls.DEVICE_LOCATION_PREFIX}:{device_id}"
            status_key = f"{cls.DEVICE_STATUS_PREFIX}:{device_id}"
            
            location_deleted = await redis_client.delete(location_key)
            status_deleted = await redis_client.delete(status_key)
            
            # 清除实时位置列表缓存
            await redis_client.delete(cls.REALTIME_LOCATIONS_KEY)
            
            logger.debug(f"清除设备缓存: {device_id}")
            return location_deleted or status_deleted
        except Exception as e:
            logger.error(f"清除设备缓存失败: {e}")
            return False
    
    @classmethod
    async def invalidate_all_cache(cls) -> bool:
        """清除所有追踪相关缓存"""
        try:
            # 获取所有相关键
            patterns = [
                f"{cls.DEVICE_LOCATION_PREFIX}:*",
                f"{cls.DEVICE_STATUS_PREFIX}:*",
                f"{cls.GEOFENCE_PREFIX}:*",
                cls.TRACKING_STATS_PREFIX,
                cls.REALTIME_LOCATIONS_KEY
            ]
            
            deleted_count = 0
            for pattern in patterns:
                if "*" in pattern:
                    # 使用scan来查找匹配的键
                    keys = []
                    async for key in redis_client.redis.scan_iter(match=pattern):
                        keys.append(key)
                    
                    if keys:
                        deleted = await redis_client.redis.delete(*keys)
                        deleted_count += deleted
                else:
                    deleted = await redis_client.delete(pattern)
                    if deleted:
                        deleted_count += 1
            
            logger.info(f"清除所有追踪缓存: {deleted_count} 个键")
            return deleted_count > 0
        except Exception as e:
            logger.error(f"清除所有追踪缓存失败: {e}")
            return False


class PubSubService:
    """发布订阅服务类"""
    
    # 频道名称
    LOCATION_UPDATE_CHANNEL = "location_updates"
    DEVICE_STATUS_CHANNEL = "device_status"
    GEOFENCE_EVENT_CHANNEL = "geofence_events"
    ALERT_CHANNEL = "alerts"
    
    @classmethod
    async def publish_location_update(
        cls, 
        device_id: str, 
        location_data: Dict[str, Any]
    ) -> bool:
        """发布位置更新"""
        try:
            message = {
                "device_id": device_id,
                "location": location_data,
                "timestamp": datetime.now().isoformat()
            }
            
            result = await redis_client.publish(
                cls.LOCATION_UPDATE_CHANNEL, 
                message
            )
            
            if result:
                logger.debug(f"发布位置更新: {device_id}")
            
            return result
        except Exception as e:
            logger.error(f"发布位置更新失败: {e}")
            return False
    
    @classmethod
    async def publish_device_status_change(
        cls, 
        device_id: str, 
        old_status: str, 
        new_status: str
    ) -> bool:
        """发布设备状态变化"""
        try:
            message = {
                "device_id": device_id,
                "old_status": old_status,
                "new_status": new_status,
                "timestamp": datetime.now().isoformat()
            }
            
            result = await redis_client.publish(
                cls.DEVICE_STATUS_CHANNEL, 
                message
            )
            
            if result:
                logger.debug(f"发布设备状态变化: {device_id} {old_status} -> {new_status}")
            
            return result
        except Exception as e:
            logger.error(f"发布设备状态变化失败: {e}")
            return False
    
    @classmethod
    async def publish_geofence_event(
        cls, 
        event_data: Dict[str, Any]
    ) -> bool:
        """发布地理围栏事件"""
        try:
            message = {
                "event": event_data,
                "timestamp": datetime.now().isoformat()
            }
            
            result = await redis_client.publish(
                cls.GEOFENCE_EVENT_CHANNEL, 
                message
            )
            
            if result:
                logger.debug(f"发布地理围栏事件: {event_data.get('event_type')}")
            
            return result
        except Exception as e:
            logger.error(f"发布地理围栏事件失败: {e}")
            return False
    
    @classmethod
    async def publish_alert(cls, alert_data: Dict[str, Any]) -> bool:
        """发布警报"""
        try:
            message = {
                "alert": alert_data,
                "timestamp": datetime.now().isoformat()
            }
            
            result = await redis_client.publish(
                cls.ALERT_CHANNEL, 
                message
            )
            
            if result:
                logger.debug(f"发布警报: {alert_data.get('title')}")
            
            return result
        except Exception as e:
            logger.error(f"发布警报失败: {e}")
            return False
